package com.jwsoft.manager.core.integration.eduAddressTree.impl;

import cn.hutool.core.convert.Convert;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.bifang.engine.assistant.core.enums.BoolEnum;
import com.jwsoft.manager.common.enums.AreaEnum;
import com.jwsoft.manager.common.vo.eduAddressTree.AddressTreeVO;
import com.jwsoft.manager.common.vo.eduAddressTree.AddressVO;
import com.jwsoft.manager.common.vo.eduAddressTree.EduAddressTreeVO;
import com.jwsoft.manager.common.vo.eduAddressTreeSchool.EduAddressTreeSchoolVO;
import com.jwsoft.manager.common.vo.eduSchool.EduSchoolVO;
import com.jwsoft.manager.core.dao.model.EduAddress;
import com.jwsoft.manager.core.dao.model.EduAddressSchool;
import com.jwsoft.manager.core.dao.model.EduAddressTree;
import com.jwsoft.manager.core.dao.model.EduAddressTreeSchool;
import com.jwsoft.manager.core.dao.service.*;
import com.jwsoft.manager.core.integration.eduAddressTree.EduAddressTreeIntegration;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Setter;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Scope;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.RecursiveTask;
import java.util.stream.Collectors;

/**
 * 地址树充血模型
 *
 * @author fangs
 */
@Setter
@Component
@Scope("prototype")
public class AddressTree {

    /**
     * spring上下文
     * 由com.jwsoft.manager.core.integration.eduAddressTree.impl.EduAddressTreeIntegrationImpl的init方法初始化
     *
     * @see EduAddressTreeIntegrationImpl#init()
     */
    protected static ApplicationContext context;

    public static AddressTree getAddressTree() {
        return context.getBean(AddressTree.class);
    }

    public static AddressTree getAddressTree(AddressTreeVO addressTreeVO) {
        AddressTree bean = context.getBean(AddressTree.class);
        bean.setAddressTreeVO(addressTreeVO);
        return bean;
    }

    public static AddressTree getAddressTree(EduAddressTree eduAddressTree) {
        AddressTreeVO vo = Convert.convert(AddressTreeVO.class, eduAddressTree);
        AddressTree bean = context.getBean(AddressTree.class);
        bean.setAddressTreeVO(vo);
        return bean;
    }

    public static AddressTree getAddressTree(EduAddressTreeVO addressTreeVO) {
        AddressTreeVO vo = Convert.convert(AddressTreeVO.class, addressTreeVO);
        AddressTree bean = context.getBean(AddressTree.class);
        bean.setAddressTreeVO(vo);
        return bean;
    }

    @Autowired
    EduAddressService eduAddressService;
    @Autowired
    EduAddressSchoolService eduAddressSchoolService;
    @Autowired
    EduAddressTreeService eduAddressTreeService;
    @Autowired
    EduAddressTreeIntegration eduAddressTreeIntegration;
    @Autowired
    EduAddressTreeSchoolService eduAddressTreeSchoolService;
    @Autowired
    EduSchoolService eduSchoolService;
    @Autowired
    DataSourceTransactionManager transactionManager;


    /**
     * searchTerm（检索项）的分割符
     */
    public static final String SEARCH_TERM_SEPARATOR = "#";

    /**
     * eduAddressId的分割符
     */
    public static final String EDU_ADDRESS_ID_SEPARATOR = ",";

    private AddressTreeVO addressTreeVO;

    @Schema(name = "childNode", title = "子集节点")
    private List<AddressTree> childNode;

    @Schema(name = "addressVO", title = "当前节点的地址信息")
    private AddressVO addressVO;

    @Schema(name = "schoolList", title = "绑定的学校列表")
    private List<EduAddressTreeSchoolVO> schoolList;

    @Schema(name = "parentNode", title = "上级节点")
    private AddressTree parentNode;

    public AddressTreeVO getAddressTreeVO() {
        return addressTreeVO;
    }

    /**
     * 修正当前节点叶子节点数
     *
     * @author fangs
     */
    public void reviseLeafNum() {
        QueryWrapper<EduAddressTree> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().select(EduAddressTree::getAddressId, EduAddressTree::getLeafNum, EduAddressTree::getLeafNode)
                .eq(EduAddressTree::getParentId, addressTreeVO.getAddressId());
        List<EduAddressTree> list = eduAddressTreeService.list(queryWrapper);
        list = list.stream().peek(item -> {
            if (item.getLeafNum() == null) {
                item.setLeafNum(0);
            }
        }).collect(Collectors.toList());
        int leafNum = 0;
        if (!CollectionUtils.isEmpty(list)) {
            for (EduAddressTree eduAddressTree : list) {
                leafNum += eduAddressTree.getLeafNum();
            }
        }
        if (addressTreeVO.getLeafNode() == 1) {
            leafNum++;
        }
        UpdateWrapper<EduAddressTree> updateWrapper = new UpdateWrapper<>();
        updateWrapper.lambda().eq(EduAddressTree::getAddressId, addressTreeVO.getAddressId())
                .set(EduAddressTree::getLeafNum, leafNum);
        eduAddressTreeService.update(updateWrapper);
        addressTreeVO.setLeafNum(leafNum);
    }

    /**
     * 修正当前节点及其上级节点个学校的节点绑定数
     *
     * @return 返回 key为学校id，value为绑定的节点数
     * @author fangs
     */
    public Map<String, Integer> reviseLeafNumOfSchool() {
        List<AddressTree> childList = getChildNode();
        //用于记录个学校绑定节点的个数，int[0]为下级节点绑定的学校数量，int[1]为叶子节点绑定的学校数量
        Map<String, int[]> schoolLeafNumMap = new HashMap<>();
        //遍历子节点，将子节点的绑定节点数相加
        for (AddressTree addressTree : childList) {
            List<EduAddressTreeSchoolVO> list = addressTree.getSchoolList();
            for (EduAddressTreeSchoolVO treeSchoolVO : list) {
                Integer boundLeafNum = treeSchoolVO.getBoundLeafNum();
                if (boundLeafNum == null) {
                    boundLeafNum = 0;
                }
                //int[0]为下级节点绑定的学校数量，int[1]为叶子节点绑定的学校数量
                int[] boundNum = schoolLeafNumMap.get(treeSchoolVO.getSchoolId());
                if (boundNum != null) {
                    boundNum[0]++;
                    boundNum[1] += boundLeafNum;
                } else {
                    boundNum = new int[2];
                    boundNum[0] = 1;
                    boundNum[1] = boundLeafNum;
                }
                schoolLeafNumMap.put(treeSchoolVO.getSchoolId(), boundNum);
            }
        }
        //如果当前节点是叶子节点，需要将当前节点绑定的学校数量加1
        if (addressTreeVO.getLeafNode() == 1) {
            List<String> leafSchool = getLeafSchool();
            for (String schoolId : leafSchool) {
                int[] boundNum = schoolLeafNumMap.computeIfAbsent(schoolId, k -> new int[2]);
                boundNum[1]++;
            }
        }
        Map<String, Integer> data = new HashMap<>((int) (schoolLeafNumMap.size() / 0.75D + 1));
        //提取当前叶子节点绑定的各学校叶子节点数量
        schoolLeafNumMap.forEach((k, v) -> {
            data.put(k, v[1]);
        });
        List<EduAddressTreeSchoolVO> list = getSchoolList();
        Map<String, Long> schoolData =
                list.stream().collect(Collectors.toMap(EduAddressTreeSchoolVO::getSchoolId, EduAddressTreeSchoolVO::getId));
        for (String schoolId : schoolLeafNumMap.keySet()) {
            int[] boundNum = schoolLeafNumMap.get(schoolId);
            if (schoolData.containsKey(schoolId)) {
                UpdateWrapper<EduAddressTreeSchool> updateWrapper = new UpdateWrapper<>();
                updateWrapper.lambda().eq(EduAddressTreeSchool::getId, schoolData.get(schoolId))
                        .set(EduAddressTreeSchool::getBoundNextNum, boundNum[0])
                        .set(EduAddressTreeSchool::getBoundLeafNum, boundNum[1]);
                eduAddressTreeSchoolService.update(updateWrapper);
                schoolData.remove(schoolId);
            } else {
                EduSchoolVO school = eduSchoolService.getDetail(schoolId);
                EduAddressTreeSchoolVO treeSchoolVO = eduAddressTreeSchoolService.saveTreeAndSchool(addressTreeVO, school, boundNum[0], boundNum[1]);
                //返回空表示已经到了不需要保存学校信息的高层节点
                if (treeSchoolVO == null) {
                    return data;
                }
                list.add(treeSchoolVO);
            }
        }
        //schoolData中剩下的节点说明子级节点中不存在，需要删除
        if (!CollectionUtils.isEmpty(schoolData)) {
            UpdateWrapper<EduAddressTreeSchool> delUpdateWrapper = new UpdateWrapper<>();
            delUpdateWrapper.lambda().in(EduAddressTreeSchool::getSchoolId, schoolData.keySet())
                    .eq(EduAddressTreeSchool::getTreeId, addressTreeVO.getAddressId())
                    .setSql("del_uk=id")
                    .set(EduAddressTreeSchool::getDel, BoolEnum.TRUE.getType());
            eduAddressTreeSchoolService.update(delUpdateWrapper);
        }

        return data;
    }

    public Integer getLeafNum() {
        return addressTreeVO.getLeafNum();
    }

    public void populate(Long addressId) {
        EduAddressTree eduAddressTree = eduAddressTreeService.getById(addressId);
        addressTreeVO = Convert.convert(AddressTreeVO.class, eduAddressTree);
    }

    public String getStreetCode() {
        if (addressVO == null) {
            addressVO = eduAddressTreeIntegration.nodeToAddressVO(addressTreeVO);
        }
        return addressVO.getTownCode();
    }

    public AddressVO getAddressVO() {
        if (addressVO == null) {
            AddressTreeVO addressTreeVO = getAddressTreeVO();
            addressVO = eduAddressTreeIntegration.nodeToAddressVO(addressTreeVO);
        }
        return addressVO;
    }

    public List<AddressTree> getChildNode() {
        if (childNode == null) {
            QueryWrapper<EduAddressTree> queryWrapper = new QueryWrapper<>();
            queryWrapper.lambda().eq(EduAddressTree::getParentId, addressTreeVO.getAddressId());
            List<EduAddressTree> list = eduAddressTreeService.list(queryWrapper);
            childNode = new ArrayList<>();
            if (!CollectionUtils.isEmpty(list)) {
                for (EduAddressTree eduAddressTree : list) {
                    AddressTree addressTree = AddressTree.getAddressTree(eduAddressTree);
                    childNode.add(addressTree);
                }
            }
        }
        return childNode;
    }

    /**
     * 获取与当前节点绑定的学校列表
     *
     * @return
     * @author fangs
     */
    public List<EduAddressTreeSchoolVO> getSchoolList() {
        if (schoolList == null) {
            QueryWrapper<EduAddressTreeSchool> queryWrapper = new QueryWrapper<>();
            queryWrapper.lambda().eq(EduAddressTreeSchool::getTreeId, addressTreeVO.getAddressId());
            List<EduAddressTreeSchool> list = eduAddressTreeSchoolService.list(queryWrapper);
            if (CollectionUtils.isEmpty(list)) {
                schoolList = new ArrayList<>();
            } else {
                schoolList = Convert.toList(EduAddressTreeSchoolVO.class, list);
            }
        }
        return schoolList;
    }

    /**
     * 获取当前节点对应地址所关联的学校列表
     *
     * @return 学校列表
     * @author fangs
     */
    public List<String> getLeafSchool() {
        if (addressTreeVO.getLeafNode() == 0) {
            return new ArrayList<>();
        }
        QueryWrapper<EduAddress> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(EduAddress::getAddressTreeId, addressTreeVO.getAddressId());
        List<EduAddress> list = eduAddressService.list(queryWrapper);
        if (CollectionUtils.isEmpty(list)) {
            return new ArrayList<>();
        }
        List<String> addressIds = list.stream().map(EduAddress::getAddressId).collect(Collectors.toList());
        QueryWrapper<EduAddressSchool> schoolQueryWrapper = new QueryWrapper<>();
        schoolQueryWrapper.lambda().in(EduAddressSchool::getAddressId, addressIds)
                .select(EduAddressSchool::getSchoolId);
        List<EduAddressSchool> schoolList = eduAddressSchoolService.list(schoolQueryWrapper);
        if (CollectionUtils.isEmpty(schoolList)) {
            return new ArrayList<>();
        }
        return schoolList.stream().map(EduAddressSchool::getSchoolId).collect(Collectors.toList());
    }

    /**
     * 同步当前节点及下级节点学区绑定数
     *
     * @author fangs
     */
    public void syncBoundSchoolNum() {
        int availableProcessors = Runtime.getRuntime().availableProcessors() / 2;
        if (availableProcessors == 0) {
            availableProcessors = 1;
        }
        ForkJoinPool forkJoinPool = new ForkJoinPool(availableProcessors);
        SyncBoundSchoolTask syncBoundSchoolTask = new SyncBoundSchoolTask(this);
        forkJoinPool.invoke(syncBoundSchoolTask);
    }

    /**
     * 保存节点绑定学校数量信息
     *
     * @param data 学校集合，int[0]为下级节点绑定的学校数量，int[1]为叶子节点绑定的学校数量
     * @author fangs
     */
    public void saveBoundSchoolData(Map<String, int[]> data) {
        //更新当前节点关联数据
        Map<String, int[]> saveMap = new HashMap<>(data);
        for (EduAddressTreeSchoolVO eduAddressTreeSchoolVO : schoolList) {
            int[] ints = saveMap.computeIfAbsent(eduAddressTreeSchoolVO.getSchoolId(), k -> new int[2]);
            UpdateWrapper<EduAddressTreeSchool> updateWrapper = new UpdateWrapper<>();
            updateWrapper.lambda().set(EduAddressTreeSchool::getBoundNextNum, ints[0])
                    .set(EduAddressTreeSchool::getBoundLeafNum, ints[1])
                    .eq(EduAddressTreeSchool::getId, eduAddressTreeSchoolVO.getId());
            eduAddressTreeSchoolService.update(updateWrapper);
            saveMap.remove(eduAddressTreeSchoolVO.getSchoolId());
            eduAddressTreeSchoolVO.setBoundNextNum(ints[0]);
            eduAddressTreeSchoolVO.setBoundLeafNum(ints[1]);
        }
        //新增当前节点之前没有的关联数据
        if (!CollectionUtils.isEmpty(saveMap)) {
            for (String s : saveMap.keySet()) {
                int[] boundNum = saveMap.get(s);
                EduSchoolVO school = eduSchoolService.getDetail(s);
                EduAddressTreeSchoolVO treeSchoolVO = eduAddressTreeSchoolService.saveTreeAndSchool(addressTreeVO, school, boundNum[0], boundNum[1]);
                //返回空表示已经到了不需要保存学校信息的高层节点
                if (treeSchoolVO == null) {
                    return;
                }
                schoolList.add(treeSchoolVO);
            }
        }
    }

    /**
     * 获取父级节点，如果没有父级节点就返回null
     *
     * @return 父级节点 | null 如果没有父级节点就返回null
     * @author fangs
     */
    protected AddressTree getParentNode() {
        if (parentNode == null) {
            if (addressTreeVO.getParentId() == null || addressTreeVO.getParentId() == 0) {
                return null;
            }
            EduAddressTree parent = eduAddressTreeService.getById(addressTreeVO.getParentId());
            parentNode = AddressTree.getAddressTree(parent);
        }
        return parentNode;
    }

    public void boundSchool(EduSchoolVO schoolVO) {
        if (addressTreeVO.getLastNode() != 1 || addressTreeVO.getLeafNode() != 1) {
            return;
        }
        QueryWrapper<EduAddressTreeSchool> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(EduAddressTreeSchool::getSchoolId, schoolVO.getOrgId())
                .eq(EduAddressTreeSchool::getTreeId, addressTreeVO.getAddressId());
        EduAddressTreeSchool treeSchool = eduAddressTreeSchoolService.getOne(queryWrapper);
        if (treeSchool == null) {
            //无关联数据，新增
            treeSchool = new EduAddressTreeSchool();
            BeanUtils.copyProperties(schoolVO, treeSchool);
            treeSchool.setSchoolId(schoolVO.getOrgId());
            treeSchool.setTreeId(addressTreeVO.getAddressId());
            treeSchool.setBoundNextNum(0);
            treeSchool.setBoundLeafNum(1);
            eduAddressTreeSchoolService.save(treeSchool);
        } else {
            //有关联数据,直接将叶子节点绑定数量更新为1
            UpdateWrapper<EduAddressTreeSchool> updateWrapper = new UpdateWrapper<>();
            updateWrapper.lambda().eq(EduAddressTreeSchool::getId, treeSchool.getId())
                    .set(EduAddressTreeSchool::getBoundLeafNum, 1);
            eduAddressTreeSchoolService.update(updateWrapper);
        }
        this.reviseBoundSchool(schoolVO);
    }


    public void unboundSchool(EduSchoolVO schoolVO) {
        if (addressTreeVO.getLastNode() != 1 || addressTreeVO.getLeafNode() != 1) {
            return;
        }
        QueryWrapper<EduAddressTreeSchool> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(EduAddressTreeSchool::getSchoolId, schoolVO.getOrgId())
                .eq(EduAddressTreeSchool::getTreeId, addressTreeVO.getAddressId());
        EduAddressTreeSchool treeSchool = eduAddressTreeSchoolService.getOne(queryWrapper);
        if (treeSchool == null) {
            return;
        }
        eduAddressTreeSchoolService.removeById(treeSchool.getId());
        this.reviseBoundSchool(schoolVO);
    }

    /**
     * 修正节点绑定学校数量信息
     *
     * @param schoolVO 需要绑定的学校信息
     * @author fangs
     */
    public void reviseBoundSchool(EduSchoolVO schoolVO) {
        //处理本级节点学校关联
        AddressTreeVO addressTreeVO = getAddressTreeVO();
        eduAddressTreeSchoolService.reviseSchoolNum(addressTreeVO, schoolVO);
        //绑定学校信息发生改变，清楚缓存
        schoolList = null;
        //如果当前节点是街道以上的节点，就不需要继续往上绑定了
        if (AreaEnum.town.getType().equals(addressTreeVO.getAreaType()) || AreaEnum.district.getType().equals(addressTreeVO.getAreaType())
                || AreaEnum.city.getType().equals(addressTreeVO.getAreaType()) || AreaEnum.province.getType().equals(addressTreeVO.getAreaType())) {
            return;
        }
        //获取父级节点
        AddressTree node = getParentNode();
        if (node == null) {
            return;
        }
        //给父级节点修正节点绑定学校数量信息
        node.reviseBoundSchool(schoolVO);
    }


    public Integer getLeafNode() {
        return addressTreeVO.getLeafNode();
    }

    public void setLeafNum(Integer leafNum) {
        addressTreeVO.setLeafNum(leafNum);
    }

    public Integer getLastNode() {
        return addressTreeVO.getLastNode();
    }

    protected void saveLeafNum(Integer leafNum) {
        addressTreeVO.setLeafNum(leafNum);
        UpdateWrapper<EduAddressTree> updateWrapper = new UpdateWrapper<>();
        updateWrapper.lambda().eq(EduAddressTree::getAddressId, addressTreeVO.getAddressId())
                .set(EduAddressTree::getLeafNum, leafNum);
        eduAddressTreeService.update(updateWrapper);
    }

    /**
     * 同步地址树当前节点及下级节点叶子节点数
     *
     * @author fangs
     */
    public void syncLeafNum() {
        int availableProcessors = Runtime.getRuntime().availableProcessors() / 2;
        if (availableProcessors == 0) {
            availableProcessors = 1;
        }
        ForkJoinPool forkJoinPool = new ForkJoinPool(availableProcessors);
        ComputeLeafNumTask computeLeafNumTask = new ComputeLeafNumTask(this);
        forkJoinPool.invoke(computeLeafNumTask);
    }
}

/**
 * 同步地址树学区绑定的下级节点数量和叶子节点数量任务
 *
 * @author fangs
 */
class SyncBoundSchoolTask extends RecursiveTask<Map<String, int[]>> {

    private static final long serialVersionUID = -2701389385678089140L;
    /**
     * 当前节点
     */
    AddressTree addressTree;

    public SyncBoundSchoolTask(AddressTree addressTree) {
        this.addressTree = addressTree;
    }


    @Override
    protected Map<String, int[]> compute() {
        List<EduAddressTreeSchoolVO> list = addressTree.getSchoolList();
        Map<String, int[]> data = new HashMap<>((int) (list.size() / 0.75D + 1));
        List<AddressTree> child = addressTree.getChildNode();
        //如果存在子节点，先处理子节点数据
        if (!CollectionUtils.isEmpty(child)) {
            List<SyncBoundSchoolTask> taskList = new ArrayList<>();
            for (AddressTree addressTree : child) {
                SyncBoundSchoolTask syncBoundSchoolTask = new SyncBoundSchoolTask(addressTree);
                taskList.add(syncBoundSchoolTask);
                syncBoundSchoolTask.fork();
            }
            for (SyncBoundSchoolTask syncBoundSchoolTask : taskList) {
                Map<String, int[]> childData = syncBoundSchoolTask.join();
                for (String s : childData.keySet()) {
                    //int[0]为下级节点绑定的学校数量，int[1]为叶子节点绑定的学校数量
                    int[] ints = childData.get(s);
                    int[] ints1 = data.computeIfAbsent(s, k -> new int[2]);
                    ints1[0]++;
                    ints1[1] += ints[1];
                }
            }
        }
        //如果当前节点是叶子节点，就将当前节点对应地址绑定过的学校叶子树+1
        List<String> leafSchool = addressTree.getLeafSchool();
        for (String schoolId : leafSchool) {
            int[] ints1 = data.computeIfAbsent(schoolId, k -> new int[2]);
            ints1[1]++;
        }
        //将子节点置为空，节省内存（如果是比较高层的节点调用该方法会查询出很多节点）
        addressTree.setChildNode(null);
        //更新当前节点关联数据
        addressTree.saveBoundSchoolData(data);
        return data;
    }
}

class ComputeLeafNumTask extends RecursiveTask<Integer> {

    private static final long serialVersionUID = 877014251476204514L;
    /**
     * 当前节点
     */
    AddressTree addressTree;

    public ComputeLeafNumTask(AddressTree addressTree) {
        this.addressTree = addressTree;
    }


    @Override
    protected Integer compute() {
        int leafNum = 0;
        List<AddressTree> childNode = addressTree.getChildNode();
        if (!CollectionUtils.isEmpty(childNode)) {
            List<ComputeLeafNumTask> taskList = new ArrayList<>();
            for (AddressTree tree : childNode) {
                ComputeLeafNumTask computeLeafNumTask = new ComputeLeafNumTask(tree);
                taskList.add(computeLeafNumTask);
                computeLeafNumTask.fork();
            }
            for (ComputeLeafNumTask computeLeafNumTask : taskList) {
                Integer join = computeLeafNumTask.join();
                leafNum += join;
            }
        }
        //如果当前节点是叶子节点，叶子节点数量加1
        if (addressTree.getLeafNode() == 1) {
            leafNum++;
        }
        addressTree.saveLeafNum(leafNum);
        return leafNum;
    }
}
